/*
  Copyright 2010 Semantic Discovery, Inc. (www.semanticdiscovery.com)

  This file is part of the Semantic Discovery Toolkit.

  The Semantic Discovery Toolkit is free software: you can redistribute it and/or modify
  it under the terms of the GNU Lesser General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  The Semantic Discovery Toolkit is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public License
  along with The Semantic Discovery Toolkit.  If not, see <http://www.gnu.org/licenses/>.
*/
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;

namespace SemanticDiscovery.Util
{
  /// <summary>
  /// Container for an integer that may be ambiguous.
  /// </summary>
  /// <author>Spence Koehler</author>
  public class AmbiguousInteger {
    // todo: implement the Serializable interface
    //private static final long serialVersionUID = 42L;
    private SortedSet<int> values;
    private int[] _values;

    public int[] Values {
      get
      {
        if (_values == null) {
          this._values = new int[values.Count];
          int i = 0;
          foreach(int value in values) {
            _values[i++] = value;
          }
        }
        return _values;
      }
    }

    public ISet<int> ValueSet { 
      get { return values; }
    }

    public bool IsAmbiguous { 
      get { return values.Count > 1; } 
    }
  
    public int LowValue {
      get { return Values[0]; } 
    }
    public int HighValue {
      get { return Values[Values.Length - 1]; } 
    }

    public AmbiguousInteger(int value) {
      this.values = new SortedSet<int>();
      this.values.Add(value);
    }

    /// Construct with semicolon-separated values.
    public AmbiguousInteger(string values) {
      this.values = new SortedSet<int>();
      string[] pieces = Regex.Split(values, "\\s*;\\s*");
      foreach(string piece in pieces) {
        this.values.Add(int.Parse(piece));
      }
    }

    public void SetValue(int value) {
      this.values.Add(value);
      this._values = null;
    }

    public bool IsStrictlyLessThan(AmbiguousInteger other) {
      return HighValue < other.LowValue;
    }

    public bool IsStrictlyGreaterThan(AmbiguousInteger other) {
      return LowValue > other.HighValue;
    }

    public bool IsStrictlyLessThan(int other) {
      return HighValue < other;
    }

    public bool IsStrictlyGreaterThan(int other) {
      return LowValue > other;
    }

    public bool IsLooselyLessThan(AmbiguousInteger other) {
      return LowValue < other.HighValue;
    }

    public bool IsLooselyGreaterThan(AmbiguousInteger other) {
      return HighValue > other.LowValue;
    }

    public bool IsLooselyLessThan(int other) {
      return LowValue < other;
    }

    public bool IsLooselyGreaterThan(int other) {
      return HighValue > other;
    }

    public bool EqualsStrictly(AmbiguousInteger other) {
      bool result = false;

      int[] myValues = Values;
      int[] otherValues = other.Values;

      if (myValues.Length == otherValues.Length) {
        result = true;
        for (int i = 0; i < myValues.Length; ++i) {
          if (myValues[i] != otherValues[i]) {
            result = false;
            break;
          }
        }
      }

      return result;    
    }

    public bool EqualsLoosely(AmbiguousInteger other) {
      bool result = false;

      int[] myValues = Values;
      int[] otherValues = other.Values;

      int i = 0;
      int j = 0;
      int myValue = myValues[i];
      int otherValue = otherValues[j];

      while (true) {
        if (myValue == otherValue) {
          result = true;
          break;
        }
        else if (myValue < otherValue) {
          ++i;
          if (i < myValues.Length) {
            myValue = myValues[i];
          }
          else {
            break;
          }
        }
        else {  // otherValue < myValue
          ++j;
          if (j < otherValues.Length) {
            otherValue = otherValues[i];
          }
          else {
            break;
          }
        }
      }

      return result;
    }

    public bool EqualsLoosely(int other) {
      bool result = false;
      foreach(int value in values) {
        if (value == other) {
          result = true;
          break;
        }
      }
      return result;
    }

    public override string ToString() {
      StringBuilder result = new StringBuilder();

      foreach(int value in values) {
        if (result.Length > 0) result.Append(';');
        result.Append(value);
      }

      return result.ToString();
    }
  }
}
